//------------------------------------------------------------------------------
//SFWK Framework
//Copyright (C) 2011 SQLI

//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IdentityModel.Policy;
using System.ServiceModel;
using System.Security.Principal;
using System.Threading;
using SFWK.Core.Cryptography;

namespace SFWK.Core.Service
{
    /// <summary>
    /// Class call to authenticate the user.
    /// 
    /// User is get from WCF HTTP-header (setted by ServiceProvider).
    /// 
    /// To use this functionality, the following elements must be placed in web.config :
    /// 
    ///  <behavior name="<Domain>.Services.S<Domain>Behavior">
    ///         <serviceAuthorization principalPermissionMode="Custom">
    ///         <authorizationPolicies>
    ///             <add policyType="SFWK.Core.Service.SFWKAuthorizationPolicy, SFWK.Core"></add>
    ///         </authorizationPolicies>
    ///     </serviceAuthorization>
    /// </behavior>
    ///
    /// </summary>
    public class SFWKAuthorizationPolicy : IAuthorizationPolicy
    {
        /// <summary>
        /// Password used to crypt the header through the calls
        /// </summary>
        public static readonly string HeaderCryptPassword = "HeaderEncryptedByPower! ;-)";

        public static readonly string HeaderName = "CLIENT_CREDENTIALS";
        public static readonly string HeaderNamespace = "SFWK";

        /// <summary>
        /// Evaluates whether a user meets the requirements for this authorization policy.
        /// </summary>
        /// <param name="evaluationContext">An <see cref="T:System.IdentityModel.Policy.EvaluationContext"/> that contains the claim set that the authorization policy evaluates.</param>
        /// <param name="state">A <see cref="T:System.Object"/>, passed by reference that represents the custom state for this authorization policy.</param>
        /// <returns>
        /// false if the <see cref="M:System.IdentityModel.Policy.IAuthorizationPolicy.Evaluate(System.IdentityModel.Policy.EvaluationContext,System.Object@)"/> method for this authorization policy must be called if additional claims are added by other authorization policies to <paramref name="evaluationContext"/>; otherwise, true to state no additional evaluation is required by this authorization policy.
        /// </returns>
        public bool Evaluate(EvaluationContext evaluationContext, ref object state)
        {
            try
            {
                string headerValue = CryptographyHelper.Decrypt(OperationContext.Current.IncomingMessageHeaders.GetHeader<string>(HeaderName, HeaderNamespace), HeaderCryptPassword);

                string[] values = headerValue.Split('|'); //split username|roles

                //store the identity to the current Thread
                GenericIdentity MyIdentity = new GenericIdentity(values[0]);
                evaluationContext.Properties["Principal"] = new GenericPrincipal(MyIdentity, values[1].Split(','));
            }
            catch (Exception e)
            {
                //in case of exception, then keep the previous authentication
                LogHelper.Log("Unable to retrieve the Authentication from HTTP-Header", e, ELogSeverity.Information);
                evaluationContext.Properties["Principal"] = Thread.CurrentPrincipal;
            }
            return true;
        }

        /// <summary>
        /// Gets a claim set that represents the issuer of the authorization policy.
        /// </summary>
        /// <value></value>
        /// <returns>A <see cref="T:System.IdentityModel.Claims.ClaimSet"/> that represents the issuer of the authorization policy.</returns>
        public System.IdentityModel.Claims.ClaimSet Issuer
        {
            get { throw new NotImplementedException(); }
        }

        /// <summary>
        /// Gets a string that identifies this authorization component.
        /// </summary>
        /// <value></value>
        /// <returns>A string that identifies this authorization component.</returns>
        public string Id
        {
            get { throw new NotImplementedException(); }
        }
    }
}

